<script setup lang="ts">
import { computed, onMounted, reactive, ref } from "vue";
import { ElMessageBox, type FormInstance, type FormRules } from "element-plus";
import { PureTableBar } from "@/components/RePureTableBar";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { message } from "@/utils/message";
import categoryApi, {
  type CategoryListItem,
  type CategoryListParams,
  type CategoryTreeNode
} from "@/api/system/product/category";

import Plus from "~icons/ep/plus";
import Refresh from "~icons/ep/refresh";
import EditPen from "~icons/ep/edit-pen";
import Delete from "~icons/ep/delete";
import Expand from "~icons/ep/expand";
import Fold from "~icons/ep/fold";

defineOptions({ name: "SystemProductCategory" });

interface CategoryFilterForm {
  keyword: string;
  is_visible: string | number;
  parent_id: string | number;
}

interface CategoryFormModel {
  id: number | null;
  name: string;
  slug: string;
  parent_id: number;
  icon_url: string;
  cover_url: string;
  description: string;
  content: string;
  sort: number;
  is_visible: boolean;
}

const DEFAULT_FILTER: CategoryFilterForm = {
  keyword: "",
  is_visible: "",
  parent_id: ""
};

const VISIBILITY_OPTIONS = [
  { label: "全部展示状态", value: "" },
  { label: "展示", value: 1 },
  { label: "隐藏", value: 0 }
];

const filterForm = reactive<CategoryFilterForm>({
  ...DEFAULT_FILTER
});

const dialogVisible = ref(false);
const submitting = ref(false);
const tableLoading = ref(false);
const tableRef = ref();
const tableData = ref<CategoryTreeNode[]>([]);
const tableKey = ref(0);
const defaultExpandAll = ref(true);

const categoryTree = ref<CategoryTreeNode[]>([]);
const MAX_CATEGORY_LEVEL = 5;

const columns: TableColumnList = [
  {
    label: "类目名称",
    prop: "name",
    minWidth: 220,
    align: "left"
  },
  {
    label: "编码",
    prop: "slug",
    minWidth: 160
  },
  {
    label: "父级类目",
    prop: "parent_id",
    minWidth: 160,
    formatter: (row: CategoryListItem) => parentLabel(row.parent_id)
  },
  {
    label: "层级",
    prop: "level",
    width: 80
  },
  {
    label: "排序",
    prop: "sort",
    width: 90
  },
  {
    label: "展示状态",
    prop: "is_visible",
    width: 120,
    slot: "visible"
  },
  {
    label: "更新时间",
    prop: "update_time",
    width: 180
  },
  {
    label: "操作",
    width: 300,
    fixed: "right",
    slot: "operation"
  }
];

const formRef = ref<FormInstance>();
const dialogTitle = ref("新增类目");

const formModel = reactive<CategoryFormModel>({
  id: null,
  name: "",
  slug: "",
  parent_id: 0,
  icon_url: "",
  cover_url: "",
  description: "",
  content: "",
  sort: 0,
  is_visible: true
});

const formRules: FormRules<CategoryFormModel> = {
  name: [
    { required: true, message: "请输入类目名称", trigger: "blur" },
    {
      min: 1,
      max: 150,
      message: "名称长度需在 1-150 字内",
      trigger: "blur"
    }
  ],
  slug: [
    { required: true, message: "请输入类目编码", trigger: "blur" },
    {
      pattern: /^[a-z0-9_-]+$/,
      message: "编码仅支持小写字母、数字、下划线和短横线",
      trigger: "blur"
    }
  ],
  sort: [
    {
      type: "number",
      transform: value => Number(value),
      message: "排序必须为数字",
      trigger: "blur"
    }
  ]
};

const flattenTree = (nodes: CategoryTreeNode[]): CategoryTreeNode[] => {
  const result: CategoryTreeNode[] = [];
  const traverse = (items: CategoryTreeNode[]) => {
    items.forEach(item => {
      result.push(item);
      if (Array.isArray(item.children) && item.children.length > 0) {
        traverse(item.children);
      }
    });
  };
  traverse(nodes ?? []);
  return result;
};

const categoryMap = computed(() => {
  const map = new Map<number, CategoryTreeNode>();
  flattenTree(categoryTree.value).forEach(item => {
    map.set(item.id, item);
  });
  return map;
});

const disallowedParentIds = computed(() => {
  if (!formModel.id) {
    return new Set<number>();
  }

  const current = categoryMap.value.get(formModel.id);
  if (!current) {
    return new Set<number>();
  }

  const forbidden = new Set<number>();
  const currentPath = current.path ?? "";

  flattenTree(categoryTree.value).forEach(item => {
    if (item.id === current.id) {
      forbidden.add(item.id);
      return;
    }
    if (currentPath !== "" && (item.path ?? "").startsWith(currentPath)) {
      forbidden.add(item.id);
    }
  });

  return forbidden;
});

const treeSelectLabel = (node: CategoryTreeNode, parentLabel: string) => {
  const name = node.name ?? `类目 ${node.id}`;
  return parentLabel ? `${parentLabel} / ${name}` : name;
};

const buildTreeSelect = (
  nodes: CategoryTreeNode[],
  parentLabel = "",
  disabledSet: Set<number>,
  disableLevelLimit = true
): any[] => {
  return nodes.map(node => {
    const label = treeSelectLabel(node, parentLabel);
    const isLevelLocked = disableLevelLimit && node.level >= MAX_CATEGORY_LEVEL;
    const option: any = {
      value: node.id,
      label,
      disabled: disabledSet.has(node.id) || isLevelLocked
    };
    if (Array.isArray(node.children) && node.children.length > 0) {
      option.children = buildTreeSelect(
        node.children,
        label,
        disabledSet,
        disableLevelLimit
      );
    }
    return option;
  });
};

const parentTreeOptions = computed(() => {
  const disabledSet = disallowedParentIds.value;
  return [
    {
      value: 0,
      label: "顶级类目",
      disabled: false,
      children: buildTreeSelect(categoryTree.value, "", disabledSet)
    }
  ];
});

const filterTreeOptions = computed(() => [
  {
    value: 0,
    label: "顶级类目",
    disabled: false,
    children: buildTreeSelect(
      categoryTree.value,
      "",
      new Set<number>(),
      false
    )
  }
]);

const parentLabel = (parentId: number) => {
  if (!parentId) {
    return "顶级类目";
  }
  return categoryMap.value.get(parentId)?.name ?? "-";
};

const adaptFilters = (): CategoryListParams => {
  const params: CategoryListParams = {};
  if (filterForm.keyword) params.keyword = filterForm.keyword.trim();
  if (filterForm.is_visible !== "" && filterForm.is_visible !== null)
    params.is_visible = Number(filterForm.is_visible);
  if (
    filterForm.parent_id !== "" &&
    filterForm.parent_id !== null &&
    filterForm.parent_id !== undefined
  )
    params.parent_id = Number(filterForm.parent_id);
  return params;
};

const fetchTableData = async () => {
  tableLoading.value = true;
  try {
    tableData.value = await categoryApi.fetchTree(adaptFilters());
  } catch (error: any) {
    tableData.value = [];
    message(error?.message ?? "获取类目数据失败", { type: "error" });
  } finally {
    tableLoading.value = false;
  }
};

const loadCategoryTree = async () => {
  try {
    categoryTree.value = await categoryApi.fetchTree();
  } catch {
    categoryTree.value = [];
  }
};

const onSearch = async () => {
  await fetchTableData();
};

const resetFilters = async () => {
  Object.assign(filterForm, { ...DEFAULT_FILTER });
  await fetchTableData();
};

const toggleTableExpandAll = () => {
  defaultExpandAll.value = !defaultExpandAll.value;
  tableKey.value += 1;
};

const resetFormModel = () => {
  formModel.id = null;
  formModel.name = "";
  formModel.slug = "";
  formModel.parent_id = 0;
  formModel.icon_url = "";
  formModel.cover_url = "";
  formModel.description = "";
  formModel.content = "";
  formModel.sort = 0;
  formModel.is_visible = true;
};

const openCreate = (parentId?: number) => {
  resetFormModel();
  const targetParent =
    typeof parentId === "number"
      ? parentId
      : (Number(filterForm.parent_id) || 0);
  formModel.parent_id = targetParent;
  dialogTitle.value = targetParent ? "新增子类目" : "新增类目";
  dialogVisible.value = true;
};

const openEdit = async (id: number) => {
  try {
    const detail = await categoryApi.get(id);
    formModel.id = detail.id;
    formModel.name = detail.name;
    formModel.slug = detail.slug;
    formModel.parent_id = detail.parent_id || 0;
    formModel.icon_url = detail.icon_url ?? "";
    formModel.cover_url = detail.cover_url ?? "";
    formModel.description = detail.description ?? "";
    formModel.content = detail.content ?? "";
    formModel.sort = detail.sort ?? 0;
    formModel.is_visible = detail.is_visible === 1;
    dialogTitle.value = "编辑类目";
    dialogVisible.value = true;
  } catch (error: any) {
    message(error?.message ?? "获取类目信息失败", { type: "error" });
  }
};

const handleDelete = async (row: CategoryListItem) => {
  try {
    await ElMessageBox.confirm(`确认删除类目「${row.name}」吗？`, "删除确认", {
      type: "warning",
      confirmButtonText: "删除",
      cancelButtonText: "取消"
    });
  } catch {
    return;
  }

  try {
    await categoryApi.remove(row.id);
    message("类目已删除", { type: "success" });
    await Promise.all([fetchTableData(), loadCategoryTree()]);
  } catch (error: any) {
    message(error?.message ?? "删除失败", { type: "error" });
  }
};

const submitForm = async () => {
  if (!formRef.value) return;
  try {
    await formRef.value.validate();
  } catch {
    return;
  }

  submitting.value = true;
  const payload = {
    name: formModel.name,
    slug: formModel.slug,
    parent_id: Number(formModel.parent_id) || 0,
    icon_url: formModel.icon_url || "",
    cover_url: formModel.cover_url || "",
    description: formModel.description || "",
    content: formModel.content || "",
    sort: Number.isFinite(Number(formModel.sort))
      ? Number(formModel.sort)
      : 0,
    is_visible: formModel.is_visible ? 1 : 0
  };

  try {
    if (formModel.id) {
      await categoryApi.update(formModel.id, payload);
      message("类目已更新", { type: "success" });
    } else {
      await categoryApi.create(payload);
      message("类目已创建", { type: "success" });
    }
    dialogVisible.value = false;
    await Promise.all([fetchTableData(), loadCategoryTree()]);
  } catch (error: any) {
    message(error?.message ?? "保存失败", { type: "error" });
  } finally {
    submitting.value = false;
  }
};

onMounted(async () => {
  await Promise.all([loadCategoryTree(), fetchTableData()]);
});
</script>
<template>
  <div class="product-category-page">
    <el-form
      :inline="true"
      :model="filterForm"
      class="search-form bg-bg_color w-full pl-4 pt-3 pb-2 overflow-auto"
    >
      <el-form-item label="关键字" prop="keyword">
        <el-input
          v-model.trim="filterForm.keyword"
          placeholder="类目名称 / 编码"
          clearable
          class="w-[220px]!"
        />
      </el-form-item>
      <el-form-item label="父级类目" prop="parent_id">
        <el-tree-select
          v-model="filterForm.parent_id"
          :data="filterTreeOptions"
          check-strictly
          clearable
          filterable
          :render-after-expand="false"
          placeholder="全部类目"
          class="w-[220px]!"
        />
      </el-form-item>
      <el-form-item label="展示状态" prop="is_visible">
        <el-select
          v-model="filterForm.is_visible"
          placeholder="全部状态"
          clearable
          class="w-[160px]!"
        >
          <el-option
            v-for="item in VISIBILITY_OPTIONS"
            :key="item.value"
            :label="item.label"
            :value="item.value"
          />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button
          type="primary"
          :icon="useRenderIcon('ri:search-line')"
          :loading="tableLoading"
          @click="onSearch"
        >
          搜索
        </el-button>
        <el-button :icon="useRenderIcon(Refresh)" @click="resetFilters">
          重置
        </el-button>
      </el-form-item>
    </el-form>

    <PureTableBar
      title="类目管理"
      :columns="columns"
      :tableRef="tableRef"
      @refresh="onSearch"
      @fullscreen="tableRef.value?.setAdaptive?.()"
    >
      <template #buttons>
        <el-button
          type="primary"
          :icon="useRenderIcon(Plus)"
          @click="openCreate()"
        >
          新增类目
        </el-button>
        <el-button
          text
          :icon="useRenderIcon(defaultExpandAll ? Fold : Expand)"
          @click="toggleTableExpandAll"
        >
          {{ defaultExpandAll ? "收起全部" : "展开全部" }}
        </el-button>
      </template>
      <template #default="{ size, dynamicColumns }">
        <PureTable
          :key="tableKey"
          ref="tableRef"
          v-loading="tableLoading"
          row-key="id"
          :columns="dynamicColumns"
          :data="tableData"
          :size="size"
          :default-expand-all="defaultExpandAll"
          :tree-props="{ children: 'children' }"
          adaptive
          :adaptiveConfig="{ offsetBottom: 56 }"
        >
          <template #visible="{ row }">
            <el-tag
              :type="row.is_visible === 1 ? 'success' : 'info'"
              size="small"
              effect="plain"
            >
              {{ row.is_visible === 1 ? "展示" : "隐藏" }}
            </el-tag>
          </template>
          <template #operation="{ row }">
            <el-button
              link
              type="primary"
              :icon="useRenderIcon(EditPen)"
              @click="openEdit(row.id)"
            >
              编辑
            </el-button>
            <el-button
              link
              type="success"
              :icon="useRenderIcon(Plus)"
              @click="openCreate(row.id)"
            >
              新增子类目
            </el-button>
            <el-button
              link
              type="danger"
              :icon="useRenderIcon(Delete)"
              @click="handleDelete(row)"
            >
              删除
            </el-button>
          </template>
        </PureTable>
      </template>
    </PureTableBar>

    <el-dialog
      v-model="dialogVisible"
      :title="dialogTitle"
      width="560px"
      destroy-on-close
    >
      <el-form
        ref="formRef"
        :model="formModel"
        :rules="formRules"
        label-width="100px"
      >
        <el-form-item label="类目名称" prop="name">
          <el-input
            v-model.trim="formModel.name"
            placeholder="请输入类目名称"
          />
        </el-form-item>
        <el-form-item label="类目编码" prop="slug">
          <el-input
            v-model.trim="formModel.slug"
            placeholder="请输入编码"
          />
        </el-form-item>
        <el-form-item label="父级类目" prop="parent_id">
          <el-tree-select
            v-model="formModel.parent_id"
            :data="parentTreeOptions"
            check-strictly
            filterable
            :render-after-expand="false"
            placeholder="选择父级类目"
            class="w-full"
          />
        </el-form-item>
        <el-form-item label="排序值" prop="sort">
          <el-input-number
            v-model="formModel.sort"
            :min="0"
            :max="9999"
            controls-position="right"
          />
        </el-form-item>
        <el-form-item label="展示状态" prop="is_visible">
          <el-switch
            v-model="formModel.is_visible"
            inline-prompt
            active-text="展示"
            inactive-text="隐藏"
          />
        </el-form-item>
        <el-form-item label="图标地址" prop="icon_url">
          <el-input
            v-model="formModel.icon_url"
            placeholder="https://"
            clearable
          />
        </el-form-item>
        <el-form-item label="封面地址" prop="cover_url">
          <el-input
            v-model="formModel.cover_url"
            placeholder="https://"
            clearable
          />
        </el-form-item>
        <el-form-item label="描述" prop="description">
          <el-input
            v-model="formModel.description"
            type="textarea"
            :rows="3"
            placeholder="类目简介，可选"
          />
        </el-form-item>
        <el-form-item label="详情备注" prop="content">
          <el-input
            v-model="formModel.content"
            type="textarea"
            :rows="4"
            placeholder="可填写类目补充说明"
          />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="dialogVisible = false">取消</el-button>
          <el-button type="primary" :loading="submitting" @click="submitForm">
            保存
          </el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>
<style scoped>
.product-category-page {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
</style>
